<?php

namespace controller\admin;

defined('IA_ROOT') || exit();

use facade\Model;
use facade\View;
use facade\Util;

// 国际化
class I18n extends Base
{
    // 翻译语言包
    public function index()
    {
        $langs = array_map(function ($item) {
            $item['title'] = Util::tran($item['title']);
            return $item;
        }, $this->langs);
        View::assign('langs', $langs);
        View::assign('lang_slugs', $this->user['langs']);
        View::assign('tran', [
            'google_translate' => Util::tran('谷歌翻译'),
            'detect_language' => Util::tran('检测语言'),
            'language_pack' => Util::tran('语言包'),
            'language' => Util::tran('语言'),
            'translate' => Util::tran('翻译'),
            'auto' => Util::tran('自动'),
            'save' => Util::tran('保存'),
            'reset' => Util::tran('重置'),
            'fetch' => Util::tran('提取'),
            'generate' => Util::tran('生成'),
            'position' => Util::tran('定位'),
            'fill' => Util::tran('填充'),
            'lines' => Util::tran('行数'),
        ]);
        View::display('admin/i18n/index.html');
    }

    // 提取语言包
    public function fetch()
    {
        if (Util::isAjax()) {
            $param = Util::param(['tl']);
            $slugs = array_map(function ($item) {
                return $item['slug'];
            }, $this->langs);
            (empty($param['tl']) || !in_array($param['tl'], $slugs)) && Util::errMsg(Util::tran('请选择目标语言'));
            $tlFile = IA_ROOT . '/mvc/locale/' . $param['tl'] . '.php';
            $todata = [];
            $data = $this->fetchData();
            if (file_exists($tlFile)) {
                $tlArr = require($tlFile);
                foreach ($data as $key) {
                    if (isset($tlArr[$key])) {
                        $todata[$key] = stripslashes($tlArr[$key]);
                    } else {
                        $todata[$key] = '';
                    }
                }
            }
            exit(json_encode(['code' => 0, 'msg' => '', 'data' => join("\n", $data), 'todata' => join("\n", $todata)]));
        }
    }

    // 生成语言包
    public function generate()
    {
        if (Util::isAjax()) {
            $param = Util::param(['sl', 'tl', 'q', 'to']);
            empty($param['sl']) && Util::errMsg(Util::tran('请选择源语言'));
            empty($param['tl']) && Util::errMsg(Util::tran('请选择目标语言'));
            (empty($param['q']) || empty($param['to'])) && Util::errMsg(Util::tran('源语言与目标语言都不能为空'));
            $slugs = array_map(function ($item) {
                return $item['slug'];
            }, $this->langs);
            ($param['sl'] !== 'zh-CN' || !in_array($param['sl'], $slugs)) && Util::errMsg(Util::tran('源语言必须是中文'));
            (!in_array($param['tl'], $slugs) || $param['tl'] === $param['sl']) && Util::errMsg(Util::tran('目标语言与源语言不能相同'));
            $q = trim($param['q']);
            $to = trim($param['to']);
            $qArr = explode("\n", $q);
            $toArr = explode("\n", $to);
            $toArr = array_map('trim', $toArr);
            (count($qArr) !== count($toArr)) && Util::errMsg(Util::tran('源语言与目标语言数量不一致'));
            $data = $this->fetchData();
            (array_diff($qArr, $data) || array_diff($data, $qArr)) && Util::errMsg(Util::tran('提取语言包与源语言不一致'));
            foreach ($toArr as $k => $v) {
                if (strlen(trim($v)) <= 0) {
                    Util::errMsg(['code' => 2, 'line' => $k + 1, 'msg' => Util::tran('目标语言包有空行，行数') . ' ' . ($k + 1)]);
                }
            }
            $tlFile = IA_ROOT . '/mvc/locale/' . $param['tl'] . '.php';
            if (is_file($tlFile)) {
                $tlArr = require($tlFile);
                $tlArr = array_values($tlArr);
                $tlArr = array_map('stripslashes', $tlArr);
                $tlArr = array_map('trim', $tlArr);
                !(array_diff($toArr, $tlArr) || array_diff($tlArr, $toArr)) && Util::errMsg(Util::tran('语言包数据未发生改变'));
            }
            $arrs = array_filter($this->langs, function ($item) use ($param) {
                return $item['slug'] === $param['tl'];
            });
            $arr = array_shift($arrs);
            $str = "<?php" . PHP_EOL;
            $str .= "/**" . PHP_EOL;
            $str .= " * @author " . addslashes($this->user['username']) . PHP_EOL;
            $str .= " * @date " . date('Y-m-d H:i:s') . PHP_EOL;
            $str .= " * @desc " . addslashes($arr['title']) . "语言包" . PHP_EOL;
            $str .= " */" . PHP_EOL;
            $str .= "return [" . PHP_EOL;
            foreach ($qArr as $k => $v) {
                $str .= '    "' . $v . '" => "' . addslashes(trim(strip_tags($toArr[$k]))) . '",' . PHP_EOL;
            }
            $str .= "];" . PHP_EOL;
            !in_array($param['tl'], $this->user['langs']) && Util::errMsg(Util::tran('目标语言权限不够，请联系管理员'));
            if (@file_put_contents(IA_ROOT . '/mvc/locale/' . $param['tl'] . '.php', $str)) {
                $this->log(Util::tran('生成语言包'), Util::tran('生成语言包') . " " . Util::tran($arr['title']) . " {$param['tl']} " . Util::tran('成功'), 0);
                Util::errMsg(['code' => 0, 'msg' => Util::tran('语言包生成成功')]);
            } else {
                $this->log(Util::tran('生成语言包'), Util::tran('生成语言包') . " " . Util::tran($arr['title']) . " {$param['tl']} " . Util::tran('失败'), 1);
                Util::errMsg(Util::tran('语言包生成失败'));
            }
        }
    }

    // 分类
    public function category()
    {
        if (Util::isAjax()) {
            $param = Util::param();
            $start = ($param['page'] - 1) * $param['limit'];
            $limit = $param['limit'];
            $where = "";
            // 搜索
            if (!empty($param['search'])) {
                foreach ((array)$param['search'] as $k => $v) {
                    if (strlen($v)) {
                        if (in_array($k, ['status', 'is_gallery']))
                            $where .= " AND `$k` = '$v'";
                        else
                            $where .= " AND `$k` like '%{$v}%'";
                    }
                }
            }
            $count = Model::fetchColumn("SELECT COUNT(*) FROM `category` WHERE 1=1 $where AND `pid` = 0 AND `lang`='zh-CN' ORDER BY `id` ASC, `sort` DESC");
            $data = Model::fetchAll("SELECT * FROM `category` WHERE 1=1 $where AND `pid` = 0 AND `lang`='zh-CN' ORDER BY `id` ASC, `sort` DESC LIMIT $start, $limit");
            $ids = array_map(function ($item) {
                return $item['id'];
            }, $data);
            $idStr = implode(',', $ids);
            if ($idStr) {
                $childs = Model::fetchAll("SELECT * FROM `category` WHERE 1=1 AND `pid` IN ($idStr) AND `lang`='zh-CN' ORDER BY `id` ASC, `sort` DESC");
                $data = array_merge($data, $childs);
            }
            $data = $this->format_tree($data);
            if ($count) {
                $res['code'] = 0;
            } else {
                $res['code'] = 1;
                $res['msg'] = Util::tran('暂无记录');
            }
            $res['count'] = $count;
            $res['data'] = array_map(function ($item) {
                if (preg_match('#(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)#i', $item['cover'], $match)) {
                    $item['cover'] = $this->site_url . $match[1];
                }
                $item['time'] = date('Y-m-d H:i', $item['time']);
                return $item;
            }, $data);
            exit(json_encode($res));
        }
        View::assign('tran', [
            'category_name' => Util::tran('分类名称'),
            'input' => Util::tran('请输入'),
            'choose' => Util::tran('请选择'),
            'slug' => Util::tran('别名'),
            'cover' => Util::tran('封面'),
            'description' => Util::tran('描述'),
            'gallery' => Util::tran('相册'),
            'status' => Util::tran('状态'),
            'normal' => Util::tran('正常'),
            'disable' => Util::tran('禁用'),
            'delall' => Util::tran('批量删除'),
            'add' => Util::tran('添加'),
            'refresh' => Util::tran('刷新'),
            'category_list' => Util::tran('分类列表'),
            'category' => Util::tran('分类'),
            'article_num' => Util::tran('文章数'),
            'sort' => Util::tran('排序'),
            'time' => Util::tran('时间'),
            'option' => Util::tran('操作'),
            'read' => Util::tran('阅读'),
            'add_sub' => Util::tran('添加下级'),
            'edit' => Util::tran('编辑'),
            'delete' => Util::tran('删除'),
            'i18n' => Util::tran('国际化'),
            'help' => Util::tran('帮助'),
        ]);
        View::display('admin/i18n/category.html');
    }

    // 分类国际化
    public function category_i18n($id = 0)
    {
        $row = Model::fetch("SELECT * FROM `category` WHERE `id`=:id", [':id' => $id]);
        (!$row || $row['lang'] !== 'zh-CN') && Util::errMsg('ID ' . Util::tran('错误'));
        if (!empty($row['cover'])) {
            if (preg_match('#(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)#i', $row['cover'], $match)) {
                $row['cover'] = $this->subdir . $match[1];
            }
        }
        !$row['slug'] && Util::errMsg(Util::tran('别名错误'));
        $param = Util::param(['tlang']);
        $tlang = isset($param['tlang']) && $param['tlang'] ? $param['tlang'] : '';
        $langs = array_map(function ($item) {
            $item['title'] = Util::tran($item['title']);
            return $item;
        }, $this->langs);
        $tlang && !array_filter($langs, function ($item) use ($tlang) {
            return $item['slug'] == $tlang;
        }) && Util::errMsg(Util::tran('语言错误'));
        $rows = Model::fetchAll("SELECT * FROM `category` WHERE `lang`='zh-CN' ORDER BY `sort` DESC, `id` ASC");
        $rows2 = Model::fetchAll("SELECT * FROM `category` WHERE `lang`='$tlang' ORDER BY `sort` DESC, `id` ASC");
        $row2 = Model::fetch("SELECT * FROM `category` WHERE `slug`=:slug AND `lang`='$tlang'", [':slug' => $row['slug']]);
        $has_langs = Model::fetchAll("SELECT `lang` FROM `category` WHERE `slug`=:slug", [':slug' => $row['slug']]);
        $has_langs = array_map(function ($item) {
            return $item['lang'];
        }, $has_langs);
        // 自动化作业
        $next = Model::fetch("SELECT * FROM `category` WHERE `lang`='zh-CN' AND `id`>:id ORDER BY `id` ASC LIMIT 1", [':id' => $id]);
        $maxId = Model::fetchColumn("SELECT MAX(`id`) FROM `category` WHERE `lang`='zh-CN' LIMIT 1");
        View::assign([
            'row' => $row,
            'row2' => $row2,
            'category' => $this->format_tree($rows),
            'category2' => $this->format_tree($rows2),
            'langs' => $langs,
            'lang_slugs' => $this->user['langs'],
            'tlang' => $tlang,
            'has_langs' => $has_langs,
            'nextId' => $next ? $next['id'] : 0,
            'maxId' => $maxId,
        ]);
        View::assign('tran', [
            'category_name' => Util::tran('分类名称'),
            'input_category_name' => Util::tran('请输入分类名称'),
            'slug' => Util::tran('别名'),
            'translate' => Util::tran('翻译'),
            'parent_category' => Util::tran('父级分类'),
            'top_category' => Util::tran('顶级分类'),
            'description' => Util::tran('描述'),
            'sort' => Util::tran('排序'),
            'input_sort' => Util::tran('请输入排序'),
            'title' => Util::tran('标题'),
            'cover' => Util::tran('封面'),
            'gallery' => Util::tran('相册'),
            'status' => Util::tran('状态'),
            'save' => Util::tran('保存'),
            'reset' => Util::tran('重置'),
            'choose' => Util::tran('请选择'),
            'back' => Util::tran('返回'),
            'language' => Util::tran('语言'),
            'not_modify' => Util::tran('不可修改'),
        ]);
        View::display('admin/i18n/category-i18n.html');
    }

    // 分类国际化保存
    public function save_category_i18n()
    {
        $param = Util::param(['_id', 'id', 'name', 'slug', 'pid', 'description', 'sort', 'seo_title', 'seo_description', 'cover', 'is_gallery', 'status', 'lang']);
        empty($param['lang']) && Util::errMsg(Util::tran('请选择语言'));
        !array_filter($this->langs, function ($item) use ($param) {
            return $item['slug'] == $param['lang'];
        }) && Util::errMsg(Util::tran('语言错误'));

        $_row = Model::fetch("SELECT * FROM `category` WHERE `id`=:_id AND `lang`='zh-CN'", [':_id' => isset($param['_id']) ? (int)$param['_id'] : 0]);
        !$_row && Util::errMsg('ID ' . Util::tran('错误'));
        unset($param['_id']);

        // 新增或修改
        $row = [];
        if (empty($param['id'])) {
            unset($param['id']);
            $param['time'] = time();
        } else {
            $row = Model::fetch("SELECT * FROM `category` WHERE `id`=:id", [':id' => $param['id']]);
            !$row && Util::errMsg('ID ' . Util::tran('错误'));
        }

        empty($param['name']) && Util::errMsg(Util::tran('分类名称为空'));
        $param['name'] = str_replace('"', '', $param['name']);

        // 重复验证
        $check = Model::fetchColumn("SELECT `name` FROM `category` WHERE `name`=:name AND `lang`=:lang ORDER BY `id`", [':name' => $param['name'], ':lang' => $param['lang']]);
        empty($row) ? $check && Util::errMsg(Util::tran('分类重名')) : $check && $check !== $row['name'] && Util::errMsg(Util::tran('分类重名'));
        empty($_row['slug']) && Util::errMsg(Util::tran('源语言别名为空，请联系源作者'));
        empty($param['slug']) && Util::errMsg(Util::tran('目标语言别名为空'));
        $_row['slug'] !== $param['slug'] && Util::errMsg(Util::tran('别名不可更改'));
        if (!empty($param['slug'])) {
            $param['slug'] = preg_replace('/[\W]/', ' ', $param['slug']);
            $param['slug'] = preg_replace('/\s+/', ' ', $param['slug']);
            $param['slug'] = strtolower($param['slug']);
            $param['slug'] = str_replace(' ', '-', $param['slug']);
            !preg_match('#^[a-z0-9_-]{2,100}$#i', $param['slug']) && Util::errMsg(Util::tran('别名只能英文字母数字下划线中划线，位数') . ' 2~100');
            $check = Model::fetchColumn("SELECT `slug` FROM `category` WHERE `slug`=:slug AND `lang`=:lang ORDER BY `id`", [':slug' => $param['slug'], ':lang' => $param['lang']]);
            empty($row) ? $check && Util::errMsg(Util::tran('别名重复')) : $check && $check !== $row['slug'] && Util::errMsg(Util::tran('别名重复'));
        }

        if (!empty($param['cover'])) {
            !preg_match('/\.(gif|jpe?g|png|bmp|webp|psd|svg|tiff)$/i', $param['cover']) && Util::errMsg(Util::tran('图片格式错误'));
            if (preg_match('#(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)#i', $param['cover'], $match)) {
                $param['cover'] = $match[1];
            }
        }

        // 更新数据库记录日志
        !in_array($this->lang, $this->user['langs']) && Util::errMsg(Util::tran('没有语言权限，请联系管理员'));

        // 修改媒体状态
        $paths = [0 => [], 1 => [], 2 => []];
        if (!empty($row) && preg_match('#(/static/upload/[^\.]+\.(gif|jpe?g|png|bmp|webp|psd|svg|tiff))#i', $row['cover'], $match)) {
            if (is_file(IA_ROOT . $match[1]))
                $paths[1][] = $match[1];
            else
                $paths[2][] = $match[1];
        }
        if (preg_match('#(/static/upload/[^\.]+\.(gif|jpe?g|png|bmp|webp|psd|svg|tiff))#i', $param['cover'], $match)) {
            if (is_file(IA_ROOT . $match[1]))
                $paths[0][] = $match[1];
            else
                $paths[2][] = $match[1];
        }
        $paths[1] = array_filter($paths[1], function ($item) use ($paths) {
            return !in_array($item, $paths[0]);
        });
        $paths[1] && Model::update('media', ['status' => 1], ['path' => array_unique($paths[1])]);
        $paths[0] && Model::update('media', ['status' => 0], ['path' => array_unique($paths[0])]);
        $paths[2] && Model::update('media', ['status' => 2], ['path' => array_unique($paths[2])]);

        $check = empty($param['id']) ? Model::insert('category', $param) : Model::update('category', $param, ['id' => $param['id']]);
        if ($check) {
            // 删除缓存
            !empty($param['id']) && $this->clearCache($param['id'], 'category', $param['lang']);
            $this->log(Util::tran('国际化分类'), Util::tran('国际化分类') . (!empty($row) ? " {$row['name']} ->" : "") . " {$param['name']} " . Util::tran('成功'), 0);
            Util::errMsg(['code' => 0, 'msg' => Util::tran('国际化分类成功')]);
        } else {
            $this->log(Util::tran('国际化分类'), !empty($row) ? Util::tran('没有改变') . " {$row['name']} " . Util::tran('信息') : Util::tran('国际化分类') . " {$param['name']} " . Util::tran('失败'), !empty($row) ? 0 : 1);
            Util::errMsg(['code' => 1, 'msg' => !empty($row) ? Util::tran('没有改变信息') : Util::tran('国际化分类失败')]);
        }
    }

    // 分类国际化帮助
    public function category_help($id = 0)
    {
        $row = Model::fetch("SELECT * FROM `category` WHERE `id`=:id", [':id' => $id]);
        !$row && Util::errMsg('ID ' . Util::tran('错误'));
        $langs = array_map(function ($item) {
            $item['title'] = Util::tran($item['title']);
            return $item;
        }, $this->langs);
        $has_langs = Model::fetchAll("SELECT `lang` FROM `category` WHERE `slug`=:slug", [':slug' => $row['slug']]);
        $has_langs = array_map(function ($item) {
            return $item['lang'];
        }, $has_langs);
        $has_langs = array_unique($has_langs);
        View::assign(['row' => $row, 'langs' => $langs, 'lang_slugs' => $this->user['langs'], 'has_langs' => $has_langs]);
        View::display('admin/i18n/help.html');
    }

    // 标签
    public function tag()
    {
        if (Util::isAjax()) {
            $param = Util::param();
            $start = ($param['page'] - 1) * $param['limit'];
            $limit = $param['limit'];
            $where = "";
            // 搜索
            if (!empty($param['search'])) {
                foreach ((array)$param['search'] as $k => $v) {
                    if (strlen($v)) {
                        if ($k == 'status')
                            $where .= " AND `$k` = '$v'";
                        else
                            $where .= " AND `$k` like '%{$v}%'";
                    }
                }
            }
            $count = Model::fetchColumn("SELECT COUNT(*) FROM `tag` WHERE 1=1 $where AND `lang`='zh-CN' ORDER BY `sort` DESC, `id` DESC");
            $data = Model::fetchAll("SELECT * FROM `tag` WHERE 1=1 $where AND `lang`='zh-CN' ORDER BY `sort` DESC, `id` DESC LIMIT $start, $limit");
            if ($count) {
                $res['code'] = 0;
            } else {
                $res['code'] = 1;
                $res['msg'] = Util::tran('暂无记录');
            }
            $res['count'] = $count;
            $res['data'] = array_map(function ($item) {
                $item['time'] = date('Y-m-d H:i', $item['time']);
                return $item;
            }, $data);
            exit(json_encode($res));
        }
        View::assign('tran', [
            'tag_name' => Util::tran('标签名称'),
            'input' => Util::tran('请输入'),
            'choose' => Util::tran('请选择'),
            'slug' => Util::tran('别名'),
            'description' => Util::tran('描述'),
            'status' => Util::tran('状态'),
            'normal' => Util::tran('正常'),
            'disable' => Util::tran('禁用'),
            'delall' => Util::tran('批量删除'),
            'add' => Util::tran('添加'),
            'refresh' => Util::tran('刷新'),
            'tag_list' => Util::tran('标签列表'),
            'tag' => Util::tran('标签'),
            'article_num' => Util::tran('文章数'),
            'sort' => Util::tran('排序'),
            'time' => Util::tran('时间'),
            'option' => Util::tran('操作'),
            'read' => Util::tran('阅读'),
            'edit' => Util::tran('编辑'),
            'delete' => Util::tran('删除'),
            'i18n' => Util::tran('国际化'),
            'help' => Util::tran('帮助'),
        ]);
        View::display('admin/i18n/tag.html');
    }

    // 标签国际化
    public function tag_i18n($id = 0)
    {
        $row = Model::fetch("SELECT * FROM `tag` WHERE `id`=:id", [':id' => $id]);
        (!$row || $row['lang'] !== 'zh-CN') && Util::errMsg('ID ' . Util::tran('错误'));
        !$row['slug'] && Util::errMsg(Util::tran('别名错误'));
        $param = Util::param(['tlang']);
        $tlang = isset($param['tlang']) && $param['tlang'] ? $param['tlang'] : '';
        $langs = array_map(function ($item) {
            $item['title'] = Util::tran($item['title']);
            return $item;
        }, $this->langs);
        $tlang && !array_filter($langs, function ($item) use ($tlang) {
            return $item['slug'] == $tlang;
        }) && Util::errMsg(Util::tran('语言错误'));
        $row2 = Model::fetch("SELECT * FROM `tag` WHERE `slug`=:slug AND `lang`='$tlang'", [':slug' => $row['slug']]);
        $has_langs = Model::fetchAll("SELECT `lang` FROM `tag` WHERE `slug`=:slug", [':slug' => $row['slug']]);
        $has_langs = array_map(function ($item) {
            return $item['lang'];
        }, $has_langs);
        // 自动化作业
        $next = Model::fetch("SELECT * FROM `tag` WHERE `lang`='zh-CN' AND `id`<:id ORDER BY `id` DESC LIMIT 1", [':id' => $id]);
        $maxId = Model::fetchColumn("SELECT MAX(`id`) FROM `tag` WHERE `lang`='zh-CN' LIMIT 1");
        View::assign([
            'row' => $row,
            'row2' => $row2,
            'langs' => $langs,
            'lang_slugs' => $this->user['langs'],
            'tlang' => $tlang,
            'has_langs' => $has_langs,
            'nextId' => $next ? $next['id'] : 0,
            'maxId' => $maxId,
        ]);
        View::assign('tran', [
            'tag_name' => Util::tran('标签名称'),
            'input_tag_name' => Util::tran('请输入标签名称'),
            'slug' => Util::tran('别名'),
            'translate' => Util::tran('翻译'),
            'description' => Util::tran('描述'),
            'sort' => Util::tran('排序'),
            'input_sort' => Util::tran('请输入排序'),
            'title' => Util::tran('标题'),
            'status' => Util::tran('状态'),
            'save' => Util::tran('保存'),
            'reset' => Util::tran('重置'),
            'choose' => Util::tran('请选择'),
            'back' => Util::tran('返回'),
            'language' => Util::tran('语言'),
            'not_modify' => Util::tran('不可修改'),
        ]);
        View::display('admin/i18n/tag-i18n.html');
    }

    // 标签国际化保存
    public function save_tag_i18n()
    {
        $param = Util::param(['_id', 'id', 'name', 'slug', 'description', 'sort', 'seo_title', 'seo_description', 'status', 'lang']);
        empty($param['lang']) && Util::errMsg(Util::tran('请选择语言'));
        !array_filter($this->langs, function ($item) use ($param) {
            return $item['slug'] == $param['lang'];
        }) && Util::errMsg(Util::tran('语言错误'));

        $_row = Model::fetch("SELECT * FROM `tag` WHERE `id`=:_id AND `lang`='zh-CN'", [':_id' => isset($param['_id']) ? (int)$param['_id'] : 0]);
        !$_row && Util::errMsg('ID ' . Util::tran('错误'));
        unset($param['_id']);

        // 新增或修改
        $row = [];
        if (empty($param['id'])) {
            unset($param['id']);
            $param['time'] = time();
        } else {
            $row = Model::fetch("SELECT * FROM `tag` WHERE `id`=:id", [':id' => $param['id']]);
            !$row && Util::errMsg('ID ' . Util::tran('错误'));
        }

        empty($param['name']) && Util::errMsg(Util::tran('标签名称为空'));
        $param['name'] = str_replace('"', '', $param['name']);

        // 重复验证
        $check = Model::fetchColumn("SELECT `name` FROM `tag` WHERE `name`=:name AND `lang`=:lang ORDER BY `id`", [':name' => $param['name'], ':lang' => $param['lang']]);
        empty($row) ? $check && Util::errMsg(Util::tran('标签重名')) : $check && $check !== $row['name'] && Util::errMsg(Util::tran('标签重名'));
        empty($_row['slug']) && Util::errMsg(Util::tran('源语言别名为空，请联系源作者'));
        empty($param['slug']) && Util::errMsg(Util::tran('目标语言别名为空'));
        $_row['slug'] !== $param['slug'] && Util::errMsg(Util::tran('别名不可更改'));
        if (!empty($param['slug'])) {
            $param['slug'] = preg_replace('/[\W]/', ' ', $param['slug']);
            $param['slug'] = preg_replace('/\s+/', ' ', $param['slug']);
            $param['slug'] = strtolower($param['slug']);
            $param['slug'] = str_replace(' ', '-', $param['slug']);
            !preg_match('#^[a-z0-9_-]{2,100}$#i', $param['slug']) && Util::errMsg(Util::tran('别名只能英文字母数字下划线中划线，位数') . ' 2~100');
            $check = Model::fetchColumn("SELECT `slug` FROM `tag` WHERE `slug`=:slug AND `lang`=:lang ORDER BY `id`", [':slug' => $param['slug'], ':lang' => $param['lang']]);
            empty($row) ? $check && Util::errMsg(Util::tran('别名重复')) : $check && $check !== $row['slug'] && Util::errMsg(Util::tran('别名重复'));
        }

        // 更新数据库记录日志
        !in_array($this->lang, $this->user['langs']) && Util::errMsg(Util::tran('没有语言权限，请联系管理员'));
        $check = empty($param['id']) ? Model::insert('tag', $param) : Model::update('tag', $param, ['id' => $param['id']]);
        if ($check) {
            // 删除缓存
            !empty($param['id']) && $this->clearCache($param['id'], 'tag', $param['lang']);
            $this->log(Util::tran('国际化标签'), Util::tran('国际化标签') . (!empty($row) ? " {$row['name']} ->" : "") . " {$param['name']} " . Util::tran('成功'), 0);
            Util::errMsg(['code' => 0, 'msg' => Util::tran('国际化标签成功')]);
        } else {
            $this->log(Util::tran('国际化标签'), !empty($row) ? Util::tran('没有改变') . " {$row['name']} " . Util::tran('信息') : Util::tran('国际化标签') . " {$param['name']} " . Util::tran('失败'), !empty($row) ? 0 : 1);
            Util::errMsg(['code' => 1, 'msg' => !empty($row) ? Util::tran('没有改变信息') : Util::tran('国际化标签失败')]);
        }
    }

    // 标签国际化帮助
    public function tag_help($id = 0)
    {
        $row = Model::fetch("SELECT * FROM `tag` WHERE `id`=:id", [':id' => $id]);
        !$row && Util::errMsg('ID ' . Util::tran('错误'));
        $langs = array_map(function ($item) {
            $item['title'] = Util::tran($item['title']);
            return $item;
        }, $this->langs);
        $has_langs = Model::fetchAll("SELECT `lang` FROM `tag` WHERE `slug`=:slug", [':slug' => $row['slug']]);
        $has_langs = array_map(function ($item) {
            return $item['lang'];
        }, $has_langs);
        $has_langs = array_unique($has_langs);
        View::assign(['row' => $row, 'langs' => $langs, 'lang_slugs' => $this->user['langs'], 'has_langs' => $has_langs]);
        View::display('admin/i18n/help.html');
    }

    // 文章
    public function article()
    {
        if (Util::isAjax()) {
            $param = Util::param();
            $start = ($param['page'] - 1) * $param['limit'];
            $limit = $param['limit'];
            // $where = "`id` <= (SELECT `id` FROM `article` WHERE `status` IN (0, 1) ORDER BY `id` DESC LIMIT $start, 1)";
            $where = "1 = 1";

            // 搜索
            if (!empty($param['search'])) {
                foreach ((array)$param['search'] as $k => $v) {
                    if (strlen($v)) {
                        if (in_array($k, ['category_id', 'status'])) {
                            $where .= " AND `$k` = '$v'";
                        } else if (in_array($k, ['start', 'end'])) {
                            $timestamp = strtotime($v);
                            if ($k == 'start')
                                $where .= " AND `create_at` >= '$timestamp'";
                            else
                                $where .= " AND `create_at` <= '$timestamp'";
                        } else {
                            $where .= " AND `$k` like '%{$v}%'";
                        }
                    }
                }
            }

            // 数据库查询
            $count = Model::fetchColumn("SELECT COUNT(*) FROM `article` WHERE $where AND `lang`='zh-CN' AND `status` IN (0, 1) ORDER BY `id` DESC");
            $data = Model::fetchAll("SELECT * FROM `article` WHERE $where AND `lang`='zh-CN' AND `status` IN (0, 1) ORDER BY `id` DESC LIMIT $start, $limit");

            // 获取分类
            $category_ids = array_map(function ($item) {
                return $item['category_id'];
            }, $data);
            $idStr = $category_ids ? implode(',', $category_ids) : 0;
            $category = Model::fetchAll("SELECT * FROM `category` WHERE `id` IN ($idStr)");
            $category = Util::formatKey($category);

            // 获取标签
            $tags = array_map(function ($item) {
                return $item['tag_ids'] ? json_decode($item['tag_ids'], true) : [];
            }, $data);
            $tag_ids = [];
            foreach ($tags as $tag) {
                $tag_ids = array_merge($tag_ids, $tag);
            }
            $tag_ids = array_unique($tag_ids);
            $idStr = $tag_ids ? implode(',', $tag_ids) : 0;
            $tag = Model::fetchAll("SELECT * FROM `tag` WHERE `id` IN ($idStr)");
            $tag = Util::formatKey($tag);

            // 分页输出
            if ($count) {
                $res['code'] = 0;
            } else {
                $res['code'] = 1;
                $res['msg'] = Util::tran('暂无记录');
            }
            $res['count'] = $count;

            $res['data'] = array_map(function ($item) use ($category, $tag) {
                // 获取分类
                $item['category'] = isset($category[$item['category_id']]) ? $category[$item['category_id']]['name'] : '';
                // 获取标签
                $item['tags'] = '';
                if ($item['tag_ids']) {
                    $tag_ids = json_decode($item['tag_ids'], true);
                    $tags = [];
                    foreach ($tag_ids as $tag_id) {
                        if (isset($tag[$tag_id]))
                            $tags[] = $tag[$tag_id]['name'];
                    }
                    $item['tags'] = implode(', ', $tags);
                }
                if (preg_match('#(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)#i', $item['cover'], $match)) {
                    $item['cover'] = $this->site_url . $match[1];
                }
                if (!$item['cover'] && preg_match_all('#<img[^>]*src=(["\'])(.*?)(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)\1#i', $item['content'], $matches, PREG_SET_ORDER)) {
                    $item['cover'] = $this->site_url . $matches[0][3];
                }
                $item['create_at'] = date('Y-m-d H:i', $item['create_at']);
                $item['update_at'] = date('Y-m-d H:i', $item['update_at']);
                return $item;
            }, $data);
            exit(json_encode($res));
        }
        // select分类无限下拉树
        $rows = Model::fetchAll("SELECT * FROM `category` WHERE `lang`='zh-CN' ORDER BY `sort` DESC, `id` ASC");
        View::assign('category_tree', $this->format_tree($rows));
        View::assign('tran', [
            'title' => Util::tran('标题'),
            'input' => Util::tran('请输入'),
            'choose' => Util::tran('请选择'),
            'slug' => Util::tran('别名'),
            'description' => Util::tran('描述'),
            'cover' => Util::tran('封面'),
            'category' => Util::tran('分类'),
            'status' => Util::tran('状态'),
            'normal' => Util::tran('正常'),
            'disable' => Util::tran('禁用'),
            'author' => Util::tran('作者'),
            'date' => Util::tran('日期'),
            'delall' => Util::tran('批量删除'),
            'add' => Util::tran('添加'),
            'refresh' => Util::tran('刷新'),
            'article_list' => Util::tran('文章列表'),
            'tag' => Util::tran('标签'),
            'view' => Util::tran('浏览'),
            'likes' => Util::tran('点赞'),
            'comment' => Util::tran('评论'),
            'create_at' => Util::tran('发布时间'),
            'update_at' => Util::tran('修改时间'),
            'option' => Util::tran('操作'),
            'read' => Util::tran('阅读'),
            'edit' => Util::tran('编辑'),
            'delete' => Util::tran('删除'),
            'i18n' => Util::tran('国际化'),
            'help' => Util::tran('帮助'),
        ]);
        View::display('admin/i18n/article.html');
    }

    // 文章国际化
    public function article_i18n($id = 0)
    {
        $row = Model::fetch("SELECT * FROM `article` WHERE `id`=:id", [':id' => $id]);
        (!$row || $row['lang'] !== 'zh-CN') && Util::errMsg('ID ' . Util::tran('错误'));
        if (preg_match('#(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)#i', $row['cover'], $match)) {
            $row['cover'] = $this->subdir . $match[1];
        }
        if (preg_match_all('#<[^>]*=(["\'])(.*?)(/static/(res|upload)/[^\s"\'<>]+?\.[^\s"\'<>]+)\1#i', $row['content'], $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                $row['content'] = str_replace($match[2] . $match[3], $this->subdir . $match[3], $row['content']);
            }
        }
        !$row['slug'] && Util::errMsg(Util::tran('别名错误'));
        $param = Util::param(['tlang']);
        $tlang = isset($param['tlang']) && $param['tlang'] ? $param['tlang'] : '';
        $langs = array_map(function ($item) {
            $item['title'] = Util::tran($item['title']);
            return $item;
        }, $this->langs);
        $tlang && !array_filter($langs, function ($item) use ($tlang) {
            return $item['slug'] == $tlang;
        }) && Util::errMsg(Util::tran('语言错误'));
        $tag_ids = $row['tag_ids'] ? json_decode($row['tag_ids'], true) : [];
        $rows = Model::fetchAll("SELECT * FROM `category` WHERE `lang`='zh-CN' ORDER BY `sort` DESC, `id` ASC");
        $trees = $this->format_tree($rows);
        $trees = array_map(function ($item) use ($rows) {
            $item['has_children'] = array_filter($rows, function ($row) use ($item) {
                return $row['pid'] == $item['id'];
            });
            return $item;
        }, $trees);
        $tags = Model::fetchAll("SELECT * FROM `tag` WHERE `lang`='zh-CN' ORDER BY `id` DESC");
        $row2 = Model::fetch("SELECT * FROM `article` WHERE `slug`=:slug AND `lang`='$tlang'", [':slug' => $row['slug']]);
        if (!empty($row2)) {
            if (preg_match('#(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)#i', $row2['cover'], $match)) {
                $row2['cover'] = $this->subdir . $match[1];
            }
            if (preg_match_all('#<[^>]*=(["\'])(.*?)(/static/(res|upload)/[^\s"\'<>]+?\.[^\s"\'<>]+)\1#i', $row2['content'], $matches, PREG_SET_ORDER)) {
                foreach ($matches as $match) {
                    $row2['content'] = str_replace($match[2] . $match[3], $this->subdir . $match[3], $row2['content']);
                }
            }
        }
        $tag_ids2 = $row2 && $row2['tag_ids'] ? json_decode($row2['tag_ids'], true) : [];
        $rows2 = Model::fetchAll("SELECT * FROM `category` WHERE `lang`='$tlang' ORDER BY `sort` DESC, `id` ASC");
        $trees2 = $this->format_tree($rows2);
        $trees2 = array_map(function ($item) use ($rows2) {
            $item['has_children'] = array_filter($rows2, function ($row2) use ($item) {
                return $row2['pid'] == $item['id'];
            });
            return $item;
        }, $trees2);
        $tags2 = Model::fetchAll("SELECT * FROM `tag` WHERE `lang`='$tlang' ORDER BY `id` DESC");
        $has_langs = Model::fetchAll("SELECT `lang` FROM `article` WHERE `slug`=:slug", [':slug' => $row['slug']]);
        $has_langs = array_map(function ($item) {
            return $item['lang'];
        }, $has_langs);
        // 自动化作业
        $next = Model::fetch("SELECT * FROM `article` WHERE `lang`='zh-CN' AND `id`<:id ORDER BY `id` DESC LIMIT 1", [':id' => $id]);
        $maxId = Model::fetchColumn("SELECT MAX(`id`) FROM `article` WHERE `lang`='zh-CN' LIMIT 1");
        View::assign([
            'row' => $row,
            'tag_ids' => $tag_ids,
            'category_tree' => $trees,
            'tags' => $tags,
            'row2' => $row2,
            'tag_ids2' => $tag_ids2,
            'category_tree2' => $trees2,
            'tags2' => $tags2,
            'langs' => $langs,
            'lang_slugs' => $this->user['langs'],
            'tlang' => $tlang,
            'has_langs' => $has_langs,
            'nextId' => $next ? $next['id'] : 0,
            'maxId' => $maxId,
        ]);
        View::assign('tran', [
            'title' => Util::tran('标题'),
            'choose' => Util::tran('请选择'),
            'artile_title' => Util::tran('文章标题'),
            'input_title' => Util::tran('请输入标题'),
            'slug' => Util::tran('别名'),
            'translate' => Util::tran('翻译'),
            'content' => Util::tran('内容'),
            'input_content' => Util::tran('请输入内容'),
            'description' => Util::tran('描述'),
            'cover' => Util::tran('封面'),
            'category' => Util::tran('分类'),
            'tag' => Util::tran('标签'),
            'multi_tag' => Util::tran('多标签半角'),
            'split' => Util::tran('分割'),
            'views' => Util::tran('浏览量'),
            'input_views' => Util::tran('请输入浏览量'),
            'likes' => Util::tran('点赞'),
            'input_likes' => Util::tran('请输入点赞数'),
            'author' => Util::tran('作者'),
            'input_author' => Util::tran('请输入作者'),
            'status' => Util::tran('状态'),
            'comment' => Util::tran('评论'),
            'save' => Util::tran('保存'),
            'reset' => Util::tran('重置'),
            'choose' => Util::tran('请选择'),
            'back' => Util::tran('返回'),
            'language' => Util::tran('语言'),
            'not_modify' => Util::tran('不可修改'),
        ]);
        View::display('admin/i18n/article-i18n.html');
    }

    // 文章国际化保存
    public function save_article_i18n()
    {
        $param = Util::param(['_id', 'id', 'title', 'slug', 'content', 'description', 'cover', 'category_id', 'tag_ids', 'tags', 'seo_title', 'seo_description', 'views', 'likes', 'author', 'status', 'can_comment', 'lang']);
        empty($param['lang']) && Util::errMsg(Util::tran('请选择语言'));
        !array_filter($this->langs, function ($item) use ($param) {
            return $item['slug'] == $param['lang'];
        }) && Util::errMsg(Util::tran('语言错误'));

        $_row = Model::fetch("SELECT * FROM `article` WHERE `id`=:_id AND `lang`='zh-CN'", [':_id' => isset($param['_id']) ? (int)$param['_id'] : 0]);
        !$_row && Util::errMsg('ID ' . Util::tran('错误'));
        unset($param['_id']);

        // 新增或修改
        $row = [];
        if (empty($param['id'])) {
            unset($param['id']);
            $param['create_at'] = time();
            $param['update_at'] = time();
        } else {
            $row = Model::fetch("SELECT * FROM `article` WHERE `id`=:id", [':id' => $param['id']]);
            !$row && Util::errMsg('ID ' . Util::tran('错误'));
            // $param['update_at'] = time();
        }

        (empty($param['title']) || empty($param['content'])) && Util::errMsg(Util::tran('标题或内容为空'));
        $param['title'] = str_replace('"', '', $param['title']);

        // 重复验证
        $check = Model::fetchColumn("SELECT `title` FROM `article` WHERE `title`=:title AND `lang`=:lang ORDER BY `id`", [':title' => $param['title'], ':lang' => $param['lang']]);
        empty($row) ? $check && Util::errMsg(Util::tran('标题重名')) : $check && $check !== $row['title'] && Util::errMsg(Util::tran('标题重名'));
        empty($_row['slug']) && Util::errMsg(Util::tran('源语言别名为空，请联系源作者'));
        empty($param['slug']) && Util::errMsg(Util::tran('目标语言别名为空'));
        $_row['slug'] !== $param['slug'] && Util::errMsg(Util::tran('别名不可更改'));
        if (!empty($param['slug'])) {
            $param['slug'] = preg_replace('/[\W]/', ' ', $param['slug']);
            $param['slug'] = preg_replace('/\s+/', ' ', $param['slug']);
            $param['slug'] = strtolower($param['slug']);
            $param['slug'] = str_replace(' ', '-', $param['slug']);
            !preg_match('#^[a-z0-9_-]{2,200}$#i', $param['slug']) && Util::errMsg(Util::tran('别名只能英文字母数字下划线中划线，位数') . ' 2~200');
            $check = Model::fetchColumn("SELECT `slug` FROM `article` WHERE `slug`=:slug AND `lang`=:lang ORDER BY `id`", [':slug' => $param['slug'], ':lang' => $param['lang']]);
            empty($row) ? $check && Util::errMsg(Util::tran('别名重复')) : $check && $check !== $row['slug'] && Util::errMsg(Util::tran('别名重复'));
        }

        if (!empty($param['cover'])) {
            !preg_match('/\.(gif|jpe?g|png|bmp|webp|psd|svg|tiff)$/i', $param['cover']) && Util::errMsg(Util::tran('图片格式错误'));
            if (preg_match('#(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)#i', $param['cover'], $match)) {
                $param['cover'] = $match[1];
            }
        }

        // 检查子分类
        $check = Model::fetch("SELECT * FROM `category` WHERE `pid`=:pid AND `lang`=:lang", [':pid' => $param['category_id'], ':lang' => $param['lang']]);
        $check && Util::errMsg(Util::tran('请选择子分类'));
        if (empty($param['author']))
            $param['author'] = 'admin';
        $check = Model::fetch("SELECT `status` FROM `user` WHERE `username` = :author", [':author' => $param['author']]);
        (!$check || $check['status']) && Util::errMsg(Util::tran('作者名未注册或被禁用'));

        // 数据处理
        !in_array($this->lang, $this->user['langs']) && Util::errMsg(Util::tran('没有语言权限，请联系管理员'));
        $tag_ids = isset($param['tag_ids']) ? $param['tag_ids'] : [];
        if ($param['tags']) {
            $tags = explode(',', $param['tags']);
            $tags = array_map('trim', $tags);
            $tags = array_filter($tags);
            $tagStr = $tags ? implode("','", $tags) : '0';
            $tag = Model::fetchAll("SELECT `id`, `name` FROM `tag` WHERE `name` IN ('$tagStr') AND `lang`=:lang ORDER BY `id` DESC", [':lang' => $param['lang']]);
            if ($tag) {
                $ids = array_map(function ($item) {
                    return $item['id'];
                }, $tag);
                $tag_ids = array_merge($ids, $tag_ids);
                $tag_ids = array_unique($tag_ids);
                // 过滤已存在的标签
                $tags = array_filter($tags, function ($item) use ($tag) {
                    return !in_array($item, array_map(function ($elem) {
                        return $elem['name'];
                    }, $tag));
                });
            }
            // 添加未存在的标签
            foreach ($tags as $_tag) {
                $lastInsertId = Model::insert('tag', ['name' => $_tag, 'time' => time(), 'lang' => $param['lang']]);
                if ($lastInsertId) {
                    array_push($tag_ids, $lastInsertId);
                    $this->log(Util::tran('国际化文章时标签'), Util::tran('国际化文章时标签') . " {$_tag} " . Util::tran('成功'), 0);
                } else {
                    $this->log(Util::tran('国际化文章时标签'), Util::tran('国际化文章时标签') . " {$_tag} " . Util::tran('失败'), 1);
                }
            }
        }
        unset($param['tags']);
        if ($tag_ids) {
            $tag_ids = array_values($tag_ids);
            $tag_ids = array_map('strval', $tag_ids);
            $param['tag_ids'] = json_encode($tag_ids);
        } else
            $param['tag_ids'] = '';

        // 更新数据库记录日志
        if (preg_match_all('#<[^>]*=(["\'])(.*?)(/static/(res|upload)/[^\s"\'<>]+?\.[^\s"\'<>]+)\1#i', $param['content'], $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                $param['content'] = str_replace($match[2] . $match[3], $match[3], $param['content']);
            }
        }

        // 修改媒体状态
        $paths = [0 => [], 1 => [], 2 => []];
        if (!empty($row) && preg_match('#(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)#i', $row['cover'], $match)) {
            if (is_file(IA_ROOT . $match[1]))
                $paths[1][] = $match[1];
            else
                $paths[2][] = $match[1];
        }
        if (!empty($row) && preg_match_all('#<[^>]*=(["\'])(.*?)(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)\1#i', $row['content'], $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                if (is_file(IA_ROOT . $match[3]))
                    $paths[1][] = $match[3];
                else
                    $paths[2][] = $match[3];
            }
        }
        if (preg_match('#(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)#i', $param['cover'], $match)) {
            if (is_file(IA_ROOT . $match[1]))
                $paths[0][] = $match[1];
            else
                $paths[2][] = $match[1];
        }
        if (preg_match_all('#<[^>]*=(["\'])(.*?)(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)\1#i', $param['content'], $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                if (is_file(IA_ROOT . $match[3]))
                    $paths[0][] = $match[3];
                else
                    $paths[2][] = $match[3];
            }
        }
        $paths[1] = array_filter($paths[1], function ($item) use ($paths) {
            return !in_array($item, $paths[0]);
        });
        $paths[1] && Model::update('media', ['status' => 1], ['path' => array_unique($paths[1])]);
        $paths[0] && Model::update('media', ['status' => 0], ['path' => array_unique($paths[0])]);
        $paths[2] && Model::update('media', ['status' => 2], ['path' => array_unique($paths[2])]);
        $check = empty($param['id']) ? Model::insert('article', $param) : Model::update('article', $param, ['id' => $param['id']]);
        if ($check) {
            // 更新文章数量
            if (!empty($param['id']) && $row['category_id'] !== $param['category_id']) {
                Model::update('category', 'num = num - 1', ['id' => $row['category_id']]);
                Model::update('category', 'num = num + 1', ['id' => $param['category_id']]);
            }
            if (!empty($param['id']) && $row['status'] !== $param['status']) {
                if ($param['status'] == '1') {
                    Model::update('category', 'num = num - 1', ['id' => $param['category_id']]);
                } else if ($param['status'] == '0') {
                    Model::update('category', 'num = num + 1', ['id' => $param['category_id']]);
                }
            }
            $prev_ids = [];
            $next_ids = [];
            if (!empty($param['id']) && $row['tag_ids'] !== $param['tag_ids']) {
                $ids = [];
                if ($row['tag_ids']) {
                    $ids = json_decode($row['tag_ids'], true);
                }
                foreach ($ids as $id) {
                    if (!in_array($id, $tag_ids))
                        $prev_ids[] = $id;
                }
                foreach ($tag_ids as $id) {
                    if (!in_array($id, $ids))
                        $next_ids[] = $id;
                }
                $prev_ids && Model::update('tag', 'num = num - 1', ['id' => $prev_ids]);
                $next_ids && Model::update('tag', 'num = num + 1', ['id' => $next_ids]);
            }
            if (!empty($param['id']) && $row['status'] !== $param['status']) {
                if ($param['status'] == '1') {
                    $next_ids && Model::update('tag', 'num = num - 1', ['id' => $next_ids]);
                } else if ($param['status'] == '0') {
                    $next_ids && Model::update('tag', 'num = num + 1', ['id' => $next_ids]);
                }
            }
            empty($param['id']) && $param['category_id'] && Model::update('category', 'num = num + 1', ['id' => $param['category_id']]);
            empty($param['id']) && $tag_ids && Model::update('tag', 'num = num + 1', ['id' => $tag_ids]);
            empty($param['id']) && $param['status'] == '1' && $param['category_id'] && Model::update('category', 'num = num - 1', ['id' => $param['category_id']]);
            empty($param['id']) && $param['status'] == '1' && $tag_ids && Model::update('tag', 'num = num - 1', ['id' => $tag_ids]);
            !empty($param['id']) && Model::update('article', ['update_at' => time()], ['id' => $param['id']]);
            // 清除缓存
            !empty($param['id']) && $this->clearCache($param['id'], 'article', $param['lang']);
            $this->log(Util::tran('国际化文章'), Util::tran('国际化文章') . (!empty($row) ? " {$row['title']} ->" : "") . " {$param['title']} " . Util::tran('成功'), 0);
            Util::errMsg(['code' => 0, 'msg' => Util::tran('国际化文章成功')]);
        } else {
            $this->log(Util::tran('国际化文章'), !empty($row) ? Util::tran('没有改变') . " {$row['title']} " . Util::tran('信息') : Util::tran('国际化文章') . " {$param['title']} " . Util::tran('失败'), !empty($row) ? 0 : 1);
            Util::errMsg(['code' => 1, 'msg' => !empty($row) ? Util::tran('没有改变信息') : Util::tran('国际化文章失败')]);
        }
    }

    // 文章国际化帮助
    public function article_help($id = 0)
    {
        $row = Model::fetch("SELECT * FROM `article` WHERE `id`=:id", [':id' => $id]);
        !$row && Util::errMsg('ID ' . Util::tran('错误'));
        $langs = array_map(function ($item) {
            $item['title'] = Util::tran($item['title']);
            return $item;
        }, $this->langs);
        $has_langs = Model::fetchAll("SELECT `lang` FROM `article` WHERE `slug`=:slug", [':slug' => $row['slug']]);
        $has_langs = array_map(function ($item) {
            return $item['lang'];
        }, $has_langs);
        $has_langs = array_unique($has_langs);
        View::assign(['row' => $row, 'langs' => $langs, 'lang_slugs' => $this->user['langs'], 'has_langs' => $has_langs]);
        View::display('admin/i18n/help.html');
    }

    // 页面
    public function page()
    {
        if (Util::isAjax()) {
            $param = Util::param();
            $start = ($param['page'] - 1) * $param['limit'];
            $limit = $param['limit'];
            $where = "";

            // 搜索
            if (!empty($param['search'])) {
                foreach ((array)$param['search'] as $k => $v) {
                    if (strlen($v)) {
                        if (in_array($k, ['is_full', 'status'])) {
                            $where .= " AND `$k` = '$v'";
                        } else if (in_array($k, ['start', 'end'])) {
                            $timestamp = strtotime($v);
                            if ($k == 'start')
                                $where .= " AND `time` >= '$timestamp'";
                            else
                                $where .= " AND `time` <= '$timestamp'";
                        } else {
                            $where .= " AND `$k` like '%{$v}%'";
                        }
                    }
                }
            }

            // 数据库查询
            $count = Model::fetchColumn("SELECT COUNT(*) FROM `page` WHERE 1=1 $where AND `pid` = 0 AND `lang`='{$this->lang}' ORDER BY `id` ASC, `sort` DESC");
            $data = Model::fetchAll("SELECT * FROM `page` WHERE 1=1 $where AND `pid` = 0 AND `lang`='{$this->lang}' ORDER BY `id` ASC, `sort` DESC LIMIT $start, $limit");
            $ids = array_map(function ($item) {
                return $item['id'];
            }, $data);
            $idStr = implode(',', $ids);
            if ($idStr) {
                $childs = Model::fetchAll("SELECT * FROM `page` WHERE 1=1 $where AND `pid` IN ($idStr) AND `lang`='{$this->lang}' ORDER BY `id` ASC, `sort` DESC");
                $data = array_merge($data, $childs);
            }
            $data = $this->format_tree($data, 'title');

            // 分页输出
            if ($count) {
                $res['code'] = 0;
            } else {
                $res['code'] = 1;
                $res['msg'] = Util::tran('暂无记录');
            }
            $res['count'] = $count;
            $res['data'] = array_map(function ($item) {
                if (preg_match('#(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)#i', $item['cover'], $match)) {
                    $item['cover'] = $this->site_url . $match[1];
                }
                if (!$item['cover'] && preg_match_all('#<img[^>]*src=(["\'])(.*?)(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)\1#i', $item['content'], $matches, PREG_SET_ORDER)) {
                    $item['cover'] = $this->site_url . $matches[0][3];
                }
                $item['time'] = date('Y-m-d H:i', $item['time']);
                return $item;
            }, $data);
            exit(json_encode($res));
        }
        View::assign('tran', [
            'title' => Util::tran('标题'),
            'input' => Util::tran('请输入'),
            'choose' => Util::tran('请选择'),
            'slug' => Util::tran('别名'),
            'cover' => Util::tran('封面'),
            'full_screen' => Util::tran('宽屏'),
            'usual' => Util::tran('普通'),
            'status' => Util::tran('状态'),
            'normal' => Util::tran('正常'),
            'disable' => Util::tran('禁用'),
            'date' => Util::tran('日期'),
            'delall' => Util::tran('批量删除'),
            'add' => Util::tran('添加'),
            'refresh' => Util::tran('刷新'),
            'page_list' => Util::tran('页面列表'),
            'sort' => Util::tran('排序'),
            'time' => Util::tran('时间'),
            'option' => Util::tran('操作'),
            'read' => Util::tran('阅读'),
            'edit' => Util::tran('编辑'),
            'delete' => Util::tran('删除'),
            'i18n' => Util::tran('国际化'),
            'help' => Util::tran('帮助'),
        ]);
        View::display('admin/i18n/page.html');
    }

    // 页面国际化
    public function page_i18n($id = 0)
    {
        $row = Model::fetch("SELECT * FROM `page` WHERE `id`=:id", [':id' => $id]);
        (!$row || $row['lang'] !== 'zh-CN') && Util::errMsg('ID ' . Util::tran('错误'));
        if (preg_match('#(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)#i', $row['cover'], $match)) {
            $row['cover'] = $this->subdir . $match[1];
        }
        if (preg_match_all('#<[^>]*=(["\'])(.*?)(/static/(res|upload)/[^\s"\'<>]+?\.[^\s"\'<>]+)\1#i', $row['content'], $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                $row['content'] = str_replace($match[2] . $match[3], $this->subdir . $match[3], $row['content']);
            }
        }
        !$row['slug'] && Util::errMsg(Util::tran('别名错误'));
        $param = Util::param(['tlang']);
        $tlang = isset($param['tlang']) && $param['tlang'] ? $param['tlang'] : '';
        $langs = array_map(function ($item) {
            $item['title'] = Util::tran($item['title']);
            return $item;
        }, $this->langs);
        $tlang && !array_filter($langs, function ($item) use ($tlang) {
            return $item['slug'] == $tlang;
        }) && Util::errMsg(Util::tran('语言错误'));
        $rows = Model::fetchAll("SELECT * FROM `page` WHERE `lang`='zh-CN' ORDER BY `sort` DESC, `id` ASC");
        $rows2 = Model::fetchAll("SELECT * FROM `page` WHERE `lang`='$tlang' ORDER BY `sort` DESC, `id` ASC");
        $row2 = Model::fetch("SELECT * FROM `page` WHERE `slug`=:slug AND `lang`='$tlang'", [':slug' => $row['slug']]);
        if (!empty($row2)) {
            if (preg_match('#(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)#i', $row2['cover'], $match)) {
                $row2['cover'] = $this->subdir . $match[1];
            }
            if (preg_match_all('#<[^>]*=(["\'])(.*?)(/static/(res|upload)/[^\s"\'<>]+?\.[^\s"\'<>]+)\1#i', $row2['content'], $matches, PREG_SET_ORDER)) {
                foreach ($matches as $match) {
                    $row2['content'] = str_replace($match[2] . $match[3], $this->subdir . $match[3], $row2['content']);
                }
            }
        }
        $has_langs = Model::fetchAll("SELECT `lang` FROM `page` WHERE `slug`=:slug", [':slug' => $row['slug']]);
        $has_langs = array_map(function ($item) {
            return $item['lang'];
        }, $has_langs);
        // 自动化作业
        $next = Model::fetch("SELECT * FROM `page` WHERE `lang`='zh-CN' AND `id`>:id ORDER BY `id` ASC LIMIT 1", [':id' => $id]);
        $maxId = Model::fetchColumn("SELECT MAX(`id`) FROM `page` WHERE `lang`='zh-CN' LIMIT 1");
        View::assign([
            'row' => $row,
            'row2' => $row2,
            'page' => $this->format_tree($rows, 'title'),
            'page2' => $this->format_tree($rows2, 'title'),
            'langs' => $langs,
            'lang_slugs' => $this->user['langs'],
            'tlang' => $tlang,
            'has_langs' => $has_langs,
            'nextId' => $next ? $next['id'] : 0,
            'maxId' => $maxId,
        ]);
        View::assign('tran', [
            'page_title' => Util::tran('页面标题'),
            'choose' => Util::tran('请选择'),
            'input_page_title' => Util::tran('请输入页面标题'),
            'parent_page' => Util::tran('父级页面'),
            'top_page' => Util::tran('顶级页面'),
            'slug' => Util::tran('别名'),
            'translate' => Util::tran('翻译'),
            'content' => Util::tran('内容'),
            'input_content' => Util::tran('请输入内容'),
            'sort' => Util::tran('排序'),
            'title' => Util::tran('标题'),
            'description' => Util::tran('描述'),
            'cover' => Util::tran('封面'),
            'full_screen' => Util::tran('宽屏'),
            'status' => Util::tran('状态'),
            'save' => Util::tran('保存'),
            'reset' => Util::tran('重置'),
            'choose' => Util::tran('请选择'),
            'back' => Util::tran('返回'),
            'language' => Util::tran('语言'),
            'not_modify' => Util::tran('不可修改'),
        ]);
        View::display('admin/i18n/page-i18n.html');
    }

    // 页面国际化保存
    public function save_page_i18n()
    {
        $param = Util::param(['_id', 'id', 'title', 'pid', 'slug', 'content', 'description', 'cover', 'sort', 'seo_title', 'seo_description', 'is_full', 'status', 'lang']);
        empty($param['lang']) && Util::errMsg(Util::tran('请选择语言'));
        !array_filter($this->langs, function ($item) use ($param) {
            return $item['slug'] == $param['lang'];
        }) && Util::errMsg(Util::tran('语言错误'));

        $_row = Model::fetch("SELECT * FROM `page` WHERE `id`=:_id AND `lang`='zh-CN'", [':_id' => isset($param['_id']) ? (int)$param['_id'] : 0]);
        !$_row && Util::errMsg('ID ' . Util::tran('错误'));
        unset($param['_id']);

        // 新增或修改
        $row = [];
        if (empty($param['id'])) {
            unset($param['id']);
            $param['time'] = time();
        } else {
            $row = Model::fetch("SELECT * FROM `page` WHERE `id`=:id", [':id' => $param['id']]);
            !$row && Util::errMsg('ID ' . Util::tran('错误'));
        }

        empty($param['title']) && Util::errMsg(Util::tran('标题不能为空'));
        $param['title'] = str_replace('"', '', $param['title']);

        // 重复验证
        $check = Model::fetchColumn("SELECT `title` FROM `page` WHERE `title`=:title AND `lang`=:lang ORDER BY `id`", [':title' => $param['title'], ':lang' => $param['lang']]);
        empty($row) ? $check && Util::errMsg(Util::tran('标题重名')) : $check && $check !== $row['title'] && Util::errMsg(Util::tran('标题重名'));
        empty($_row['slug']) && Util::errMsg(Util::tran('源语言别名为空，请联系源作者'));
        empty($param['slug']) && Util::errMsg(Util::tran('目标语言别名为空'));
        $_row['slug'] !== $param['slug'] && Util::errMsg(Util::tran('别名不可更改'));
        if (!empty($param['slug'])) {
            $param['slug'] = preg_replace('/[\W]/', ' ', $param['slug']);
            $param['slug'] = preg_replace('/\s+/', ' ', $param['slug']);
            $param['slug'] = strtolower($param['slug']);
            $param['slug'] = str_replace(' ', '-', $param['slug']);
            !preg_match('#^[a-z0-9_-]{2,100}$#i', $param['slug']) && Util::errMsg(Util::tran('别名只能英文字母数字下划线中划线，位数') . ' 2~100');
            $check = Model::fetchColumn("SELECT `slug` FROM `page` WHERE `slug`=:slug AND `lang`=:lang ORDER BY `id`", [':slug' => $param['slug'], ':lang' => $param['lang']]);
            empty($row) ? $check && Util::errMsg(Util::tran('别名重复')) : $check && $check !== $row['slug'] && Util::errMsg(Util::tran('别名重复'));
        }

        if (!empty($param['cover'])) {
            !preg_match('/\.(gif|jpe?g|png|bmp|webp|psd|svg|tiff)$/i', $param['cover']) && Util::errMsg(Util::tran('图片格式错误'));
            if (preg_match('#(/static/upload/[^\s"\'<>]+?\.[^\s"\'<>]+)#i', $param['cover'], $match)) {
                $param['cover'] = $match[1];
            }
        }

        // 更新数据库记录日志
        if (preg_match_all('#<[^>]*=(["\'])(.*?)(/static/(res|upload)/[^\s"\'<>]+?\.[^\s"\'<>]+)\1#i', $param['content'], $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                $param['content'] = str_replace($match[2] . $match[3], $match[3], $param['content']);
            }
        }
        !in_array($this->lang, $this->user['langs']) && Util::errMsg(Util::tran('没有语言权限，请联系管理员'));

        // 修改媒体状态
        $paths = [0 => [], 1 => [], 2 => []];
        if (!empty($row) && preg_match_all('#<[^>]*=(["\'])(.*?)(/static/(res|upload)/[^\s"\'<>]+?\.[^\s"\'<>]+)\1#i', $row['content'], $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                if (is_file(IA_ROOT . $match[3]))
                    $paths[1][] = $match[3];
                else
                    $paths[2][] = $match[3];
            }
        }
        if (preg_match_all('#<[^>]*=(["\'])(.*?)(/static/(res|upload)/[^\s"\'<>]+?\.[^\s"\'<>]+)\1#i', $param['content'], $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                if (is_file(IA_ROOT . $match[3]))
                    $paths[0][] = $match[3];
                else
                    $paths[2][] = $match[3];
            }
        }
        $paths[1] = array_filter($paths[1], function ($item) use ($paths) {
            return !in_array($item, $paths[0]);
        });
        $paths[1] && Model::update('media', ['status' => 1], ['path' => array_unique($paths[1])]);
        $paths[0] && Model::update('media', ['status' => 0], ['path' => array_unique($paths[0])]);
        $paths[2] && Model::update('media', ['status' => 2], ['path' => array_unique($paths[2])]);
        $check = empty($param['id']) ? Model::insert('page', $param) : Model::update('page', $param, ['id' => $param['id']]);
        if ($check) {
            // 清除缓存
            !empty($param['id']) && $this->clearCache($param['id'], 'page', $param['lang']);
            $this->log(Util::tran('国际化页面'), Util::tran('国际化页面') . (!empty($row) ? " {$row['title']} ->" : "") . " {$param['title']} " . Util::tran('成功'), 0);
            Util::errMsg(['code' => 0, 'msg' => Util::tran('国际化页面成功')]);
        } else {
            $this->log(Util::tran('国际化页面'), !empty($row) ? Util::tran('没有改变') . " {$row['title']} " . Util::tran('信息') : Util::tran('国际化页面') . " {$param['title']} " . Util::tran('失败'), !empty($row) ? 0 : 1);
            Util::errMsg(['code' => 1, 'msg' => !empty($row) ? Util::tran('没有改变信息') : Util::tran('国际化页面失败')]);
        }
    }

    // 页面国际化帮助
    public function page_help($id = 0)
    {
        $row = Model::fetch("SELECT * FROM `page` WHERE `id`=:id", [':id' => $id]);
        !$row && Util::errMsg('ID ' . Util::tran('错误'));
        $langs = array_map(function ($item) {
            $item['title'] = Util::tran($item['title']);
            return $item;
        }, $this->langs);
        $has_langs = Model::fetchAll("SELECT `lang` FROM `page` WHERE `slug`=:slug", [':slug' => $row['slug']]);
        $has_langs = array_map(function ($item) {
            return $item['lang'];
        }, $has_langs);
        $has_langs = array_unique($has_langs);
        View::assign(['row' => $row, 'langs' => $langs, 'lang_slugs' => $this->user['langs'], 'has_langs' => $has_langs]);
        View::display('admin/i18n/help.html');
    }

    // 私有方法提取语言包数据
    private function fetchData()
    {
        $data = [];
        // 提取控制器
        $dirs = glob(IA_ROOT . '/controller/*');
        if ($dirs) {
            foreach ($dirs as $dir) {
                if (is_dir($dir)) {
                    foreach (glob($dir . '/*') as $file) {
                        $content = file_get_contents($file);
                        if (preg_match_all('/Util::tran\(["\'](.*?)["\'].*?\)/', $content, $matches)) {
                            $data = array_merge($data, $matches[1]);
                        }
                    }
                }
            }
        }
        // 提取设置
        $data[] = $this->setting('webName');
        $data[] = $this->setting('seo_title');
        $data[] = $this->setting('seo_description');
        $data[] = $this->setting('copyright');
        $data[] = $this->setting('icp');
        // 提取菜单
        $menus = array_map(function ($item) {
            return $item['title'];
        }, $this->auth);
        $data = array_merge($data, $menus);
        // 提取语言
        $titles = array_map(function ($item) {
            return $item['title'];
        }, $this->langs);
        $data = array_merge($data, $titles);
        // 去重整理
        $data = array_map('trim', $data);
        $data = array_filter($data);
        $data = array_unique($data);
        $data = array_values($data);
        usort($data, function ($a, $b) {
            return strlen($b) - strlen($a);
        });
        return $data;
    }

    // 删除缓存
    private function clearCache($ids = 0, $type = 'article', $lang = 'zh-CN')
    {
        if ($ids) {
            $ids = (array)$ids;
            $ids = array_unique($ids);
            $idStr = implode(',', $ids);
            $rows = Model::fetchAll("SELECT * FROM `$type` WHERE `id` IN ($idStr)");
            $prefix = $this->site_url . ($lang === 'zh-CN' ? "/" : "/{$lang}/");
            $cat_ids = [];
            $tag_ids = [];
            foreach ($rows as $row) {
                $count = 0;
                if ($type == 'article') {
                    // 分类
                    $cat_ids[] = $row['category_id'];
                    $cat_ids = array_unique($cat_ids);
                    // 标签
                    $tags = (array)json_decode($row['tag_ids'], true);
                    $tags = array_map('intval', $tags);
                    $tag_ids = array_merge($tag_ids, $tags);
                    $tag_ids = array_unique($tag_ids);
                    // 评论
                    $count = Model::fetchColumn("SELECT COUNT(*) FROM `comment` WHERE `pid`=0 AND `article_id`=:article_id AND `status`=0 ORDER BY `id` DESC", [':article_id' => $row['id']]);
                } else if ($type == 'category') {
                    $count = Model::fetchColumn("SELECT COUNT(*) FROM `article` WHERE `category_id`=:category_id AND `status`=0 ORDER BY `id` DESC", [':category_id' => $row['id']]);
                } else if ($type == 'tag') {
                    $count = Model::fetchColumn("SELECT COUNT(*) FROM `article` WHERE `tag_ids` like '%\"{$row['id']}\"%' AND `status`=0 ORDER BY `id` DESC");
                }
                $file_name = md5($prefix . ($type === 'article' ? '' : $type . '/') . ($row['slug'] ? $row['slug'] : $row['id']) . '.html');
                $cache_file = IA_ROOT . '/mvc/tpl/' . substr($file_name, 0, 2) . '/' . $file_name . '.php';
                file_exists($cache_file) && unlink($cache_file);
                $file_name = md5($prefix);
                $cache_file = IA_ROOT . '/mvc/tpl/' . substr($file_name, 0, 2) . '/' . $file_name . '.php';
                file_exists($cache_file) && unlink($cache_file);
                // 删除分页缓存
                for ($i = 2; $i <= ceil($count / 10); $i++) {
                    $file_name = md5($prefix . ($type === 'article' ? '' : $type . '/') . ($row['slug'] ? $row['slug'] : $row['id']) . '/page-' . $i . '.html');
                    $cache_file = IA_ROOT . '/mvc/tpl/' . substr($file_name, 0, 2) . '/' . $file_name . '.php';
                    file_exists($cache_file) && unlink($cache_file);
                }
                // 删除下载内容
                if ($type === 'article') {
                    foreach (glob(IA_ROOT . '/static/upload/articles/ID' . str_pad($row['id'], 7, '0', STR_PAD_LEFT) . '-*') as $file) {
                        unlink($file);
                    }
                }
            }
            $cat_ids && $this->clearCache($cat_ids, 'category', $lang);
            $tag_ids && $this->clearCache($tag_ids, 'tag', $lang);
        }
    }
}
